/*
 *    Copyright (C) 2011-2012 Hai Bison
 *
 *    See the file LICENSE at the root directory of this project for copying
 *    permission.
 */

package group.pals.mailgate.utils;

import group.pals.mailgate.DialogUpdater;
import group.pals.mailgate.preferences.Preferences;
import group.pals.threads.BaseThread;
import group.pals.threads.listeners.ThreadAdapter;
import group.pals.utils.files.FileUtils;
import group.pals.utils.sys.MessageBox;
import group.pals.utils.sys.SysUtils;
import group.pals.utils.updater.listeners.UpdateAdapter;
import group.pals.utils.updater.Updater;
import group.pals.utils.updater.beans.InfoFile;
import group.pals.utils.updater.beans.ItemUpdate;
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import org.apache.commons.lang3.StringEscapeUtils;

/**
 *
 * @author Hai Bison
 */
public class Utils {

    private Utils() {
        //
    }

    public void initialize() {
        //execute preferences manager
        Preferences.getInstance();
    }

    public static Utils getInstance() {
        return NewSingletonHolder.INSTANCE;
    }

    private static class NewSingletonHolder {

        private static final Utils INSTANCE = new Utils();
    }
    /*----------------------------------------------------------------------------
     * Updater
     */
    /**
     * The updater.
     */
    private Updater updater = null;
    /**
     * {@code true} if the user wants to update now
     */
    private boolean forceUpdate = false;
    /**
     * Default update file.
     */
    private static final String LocalUpdateFilename = "update-info";
    /**
     * The update URL.
     */
    private static final String UpdateURL =
            "https://sites.google.com/site/haitimeid/mail-gate/update-info?attredirects=0&d=1";

    public File getUpdateFile() {
        return new File(String.format("%s/%s", getStartupDir().getAbsolutePath(), LocalUpdateFilename));
    }

    /**
     * Starts {@link Updater}
     */
    public void startUpdater() {
        if (updater == null) {
            try {
                updater = new Updater(getUpdateFile(), UpdateURL, 0);
            } catch (Exception ex) {
                MessageBox.showErrMsg(null, null, String.format("Failed to start updater: %s", ex));
                return;
            }

            //update listener
            updater.addUpdateListener(new UpdateAdapter() {

                @Override
                public synchronized void update(InfoFile infoFile, List<ItemUpdate> itemUpdates) {
                    try {
                        if (itemUpdates.isEmpty()) {
                            if (forceUpdate) {
                                MessageBox.showInfoMsg(null, null, "You are using newest version");
                            }
                        } else {
                            StringBuffer msg = new StringBuffer(
                                    String.format("<html>There %s new version%s:<br><p><ul>",
                                    itemUpdates.size() == 1 ? "is" : "are",
                                    itemUpdates.size() == 1 ? "" : "s"));
                            for (ItemUpdate item : itemUpdates) {
                                msg.append(String.format("<li><b>%s</b> %s [%s]</li>",
                                        StringEscapeUtils.escapeHtml4(item.getName()),
                                        StringEscapeUtils.escapeHtml4(item.getVersion()),
                                        StringEscapeUtils.escapeHtml4(item.getBuild())));
                            }
                            msg.append("</ul></p><br>Do you want to update?");

                            JLabel labelMsg = new JLabel(msg.toString());
                            labelMsg.setFont(labelMsg.getFont().deriveFont(Font.PLAIN));
                            labelMsg.setForeground(Color.blue);

                            if (MessageBox.confirmYesNo(null, null, labelMsg, 0)) {
                                DialogUpdater du = new DialogUpdater(null, true, infoFile, itemUpdates);
                                du.start();
                            }
                        }
                    } finally {
                        forceUpdate = false;
                    }
                }
            });

            //thread listener
            updater.addThreadListener(new ThreadAdapter() {

                @Override
                public synchronized void threadRaised(BaseThread sender, Exception ex, Object obj) {
                    if (forceUpdate) {
                        MessageBox.showErrMsg(null, null, String.format("Exception when updating:\n%s", ex));
                    }
                }

                @Override
                public synchronized void threadTerminated(BaseThread sender) {
                    updater = null;
                }
            });

            //ok, now start it
            updater.start();
        }//if updater == null
    }

    /**
     * Stops {@link Updater}
     */
    public void stopUpdater() {
        if (updater != null) {
            updater.interrupt();
        }
    }

    /**
     * Restarts {@link Updater}
     */
    public void restartUpdater() {
        stopUpdater();
        startUpdater();
    }

    /**
     * Forces update!
     */
    public void forceUpdaterUpdate() {
        forceUpdate = true;
        if (updater == null) {
            startUpdater();
        } else {
            updater.doForceCheck();
        }
    }

    /*----------------------------------------------------------------------------
     * IMPLEMENTATION
     */
    /**
     * UTC time offset, in milliseconds.
     */
    public static final int UTCTimeOffset = (int) (7 * 60 * 60e3);
    /**
     * The URL of Paypal donation page
     */
    public static final String PaypalDonationURL =
            "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RA67M6S7MRJDC";

    /**
     * Gets startup directory. Can be used in IDE or runtime.
     * @return the startup directory.
     */
    public File getStartupDir() {
        return getJarFile().getParentFile();
    }

    /**
     * @return the jar file
     */
    public File getJarFile() {
        return SysUtils.getAppJar(getClass());
    }

    public String getUsername() {
        return System.getProperty("user.name");
    }

    /*----------------------------------------------------------------------------
     * Application's Attributes
     */
    public String getAppVersion() {
        String implVersion = getClass().getPackage().getImplementationVersion();
        if (implVersion == null) {
            implVersion = "";
        }
        return implVersion;
    }

    public String getAppTitle() {
        String implTitle = getClass().getPackage().getImplementationTitle();
        if (implTitle == null) {
            implTitle = "";
        }
        return implTitle;
    }

    public String getAppTitleWithVersion() {
        return String.format("%s %s", getAppTitle(), getAppVersion());
    }

    /**
     * Gets main icon (256x256)
     * @return {@link ImageIcon}
     */
    public ImageIcon getMainIcon() {
        //logo
        return getIcon(256);
    }

    /**
     * Gets icon by dimension.
     * @param dimension
     * @return {@link ImageIcon} or {@code null} if the dimension required is not available
     */
    public ImageIcon getIcon(int dimension) {
        //logo
        URL url = getClass().getClassLoader().getResource(String.format("resources/images/logos/logo_%d.png", dimension));
        if (url != null) {
            return new ImageIcon(url);
        }
        return null;
    }

    public ImageIcon getIconAttachment24xy() {
        //logo
        URL url = getClass().getClassLoader().getResource("resources/images/controls/attachment_24xy.png");
        if (url != null) {
            return new ImageIcon(url);
        }
        return null;
    }

    public List<Image> getMainIcons() {
        List<Image> result = new ArrayList<Image>();

        /* Haiti: I hate this, any JDK compiles to bytecode, why can't the old one
         * run that code?
         * For the end users, I must to code on JDK 1.5! Hic!!!  :-(
         */
        try {
            JarFile jf = new JarFile(getJarFile());
            try {
                for (Enumeration<JarEntry> entries = jf.entries(); entries.hasMoreElements();) {
                    JarEntry entry = entries.nextElement();
                    if (entry.isDirectory()) {
                        continue;
                    }
                    if (entry.getName().matches("(?sim)resources/images/logos/logo_[0-9]+\\.png")) {
                        InputStream inStream = jf.getInputStream(entry);
                        try {
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            try {
                                byte[] bytes = new byte[1024 * 99]; //99 KB  :-)
                                int read = 0;
                                while ((read = inStream.read(bytes)) > 0) {
                                    baos.write(bytes, 0, read);
                                }
                                result.add(new ImageIcon(baos.toByteArray()).getImage());
                            } finally {
                                baos.close();
                            }
                        } finally {
                            inStream.close();
                        }
                    }//if entry's name matches one of logos' name
                }//for
            } finally {
                jf.close();
            }
        } catch (Exception ex) {
        }

        return result;
    }

    /**
     * Gets about text of application.
     * @return the about text
     */
    public String getAboutText() {
        final String PatternAppName = "\\$\\(APP_NAME\\)";
        final String PatternAppVersion = "\\$\\(VERSION\\)";
        final String PatternAppBuildNumber = "\\$\\(BUILD_NUMBER\\)";

        try {
            JarFile jar = new JarFile(getJarFile());
            try {
                JarEntry entry = jar.getJarEntry("resources/about");
                byte[] buf = new byte[1024];
                InputStream inputStream = jar.getInputStream(entry);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                try {
                    int read;
                    while ((read = inputStream.read(buf)) > 0) {
                        baos.write(buf, 0, read);
                    }
                } finally {
                    inputStream.close();
                    baos.close();
                }
                String result = new String(baos.toByteArray());
                return result.replaceAll(PatternAppName, getAppTitle()).replaceAll(PatternAppVersion, getAppVersion()).replaceAll(PatternAppBuildNumber, FileUtils.getBuildNumber(getClass(), UTCTimeOffset));
            } finally {
                jar.close();
            }
        } catch (Exception ex) {
            return "";
        }
    }
    /**
     * Unique ID of JVM
     */
    public static final String JvmUniqueID = SysUtils.getJvmUniqueId();
    /**
     * Preferences filename.
     */
    public static final String PreferencesFilename = ".preferences";

    /**
     * @param width in pixels.
     * @param text the text, if it is <code>null</code>, it will be set to empty text.
     * If not, you <b>should</b> escape it to use as a html text.
     * @return the html code.
     */
    public String getLimitedTextWidthHtml(int width, String text) {
        if (text == null) {
            text = "";
        }
        return String.format("<html><p><div width=\"%d\">%s</div></p></html>", width, text);
    }

    /**
     * @param width in pixels.
     * @param text the text, if it is <code>null</code>, it will be set to empty text.
     * @param escapeHtml If <code>true</code>, the <code>text</code> will be escaped html.
     * @return the html code.
     */
    public String getLimitedTextWidthHtml(int width, String text, boolean escapeHtml) {
        if (text == null) {
            text = "";
        } else if (escapeHtml) {
            text = StringEscapeUtils.escapeHtml4(text);
        }
        return getLimitedTextWidthHtml(width, text);
    }
}
